package main

import (
	"archive/zip"
	_ "embed"
	"fmt"
	"io"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"time"
)

func main() {
	if len(os.Args) < 3 {
		fmt.Println("too low args.")
		fmt.Println("usb like this:\"bootloader 59120 server.exe\"")
		return
	}
	go RunProcess()
	http.HandleFunc("/", HttpHandle)
	err := http.ListenAndServe(":"+os.Args[1], nil)
	fmt.Println(err)
}

//go:embed index.html
var html []byte

var cmd *exec.Cmd

func RunProcess() {
	for {
		time.Sleep(5 * time.Second)
		src, err := os.Open("execFile")
		if err == nil { // 存在execFile文件，将其替换成正式文件后再运行。
			dst, err := os.Create(os.Args[2])
			if err == nil {
				io.Copy(dst, src)
				dst.Close()
				os.Chmod(os.Args[2], 0755)
			}
			src.Close()
			os.Remove("execFile")
		}
		os.MkdirAll("logs/out", 0755)
		os.MkdirAll("logs/err", 0755)
		stdout, err := os.Create("logs/out/out.log")
		if err != nil {
			fmt.Println(err)
			continue
		}
		stderr, err := os.Create("logs/err/err.log")
		if err != nil {
			fmt.Println(err)
			stdout.Close()
			continue
		}
		cmd = exec.Command(os.Args[2], os.Args[3:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		err = cmd.Start()
		if err != nil {
			fmt.Println(err)
			continue
		}
		errch := make(chan error, 1)
		go func() {
			errch <- cmd.Wait()
		}()
		loop := true
		for loop {
			select {
			case <-errch: // 进程退出后，退出本次循环，进入下次循环
				loop = false
			case <-time.After(time.Hour): // 1小时后，更换存储fmt的文件，但是进程不重启
				now := time.Now()
				os.MkdirAll("logs/out/"+now.Format("2006/01"), 0755)
				oldstdout, err := os.Open("logs/out/out.log")
				if err != nil {
					fmt.Println(err)
					continue
				}
				newstdout, err := os.Create("logs/out/" + time.Now().Format("2006/01/02_15_04_05") + ".log")
				if err != nil {
					fmt.Println(err)
					oldstdout.Close()
					continue
				}
				io.Copy(newstdout, oldstdout)
				oldstdout.Close()
				newstdout.Close()
				stdout.Truncate(0) // 清空输出文件
				stdout.Seek(0, io.SeekStart)
				os.MkdirAll("logs/err/"+now.Format("2006/01"), 0755)
				oldstderr, err := os.Open("logs/err/err.log")
				if err != nil {
					fmt.Println(err)
					continue
				}
				newstderr, err := os.Create("logs/err/" + time.Now().Format("2006/01/02_15_04_05") + ".log")
				if err != nil {
					fmt.Println(err)
					oldstderr.Close()
					continue
				}
				io.Copy(newstderr, oldstderr)
				oldstderr.Close()
				newstderr.Close()
				stderr.Truncate(0) // 清空错误文件
				stderr.Seek(0, io.SeekStart)
			}
		}
		stdout.Close()
		stderr.Close()
		now := time.Now()
		os.MkdirAll("logs/out/"+now.Format("2006/01"), 0755)
		os.Rename("logs/out/out.log", "logs/out/"+now.Format("2006/01/02_15_04_05")+".log")
		os.MkdirAll("logs/err/"+now.Format("2006/01"), 0755)
		os.Rename("logs/err/err.log", "logs/err/"+now.Format("2006/01/02_15_04_05")+".log")
	}
}

func HttpHandle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	if r.RequestURI == "/api/update" {
		err := r.ParseMultipartForm(100 * 1024 * 1024)
		if err != nil {
			w.Write([]byte("{\"errcode\":3000,\"errmsg\":" + err.Error() + "}"))
			return
		}
		src, _, err := r.FormFile("file")
		if err != nil {
			w.Write([]byte("{\"errcode\":3000,\"errmsg\":" + err.Error() + "}"))
			return
		}
		dst, err := os.Create("execFile")
		if err != nil {
			w.Write([]byte("{\"errcode\":3000,\"errmsg\":" + err.Error() + "}"))
			return
		}
		io.Copy(dst, src)
		src.Close()
		dst.Close()
		if cmd.Process != nil && cmd.Process.Pid > 0 {
			cmd.Process.Kill()
		}
		w.Write([]byte("{\"errcode\":0}"))
	} else if r.RequestURI == "/api/reset" {
		if cmd.Process != nil && cmd.Process.Pid > 0 {
			cmd.Process.Kill()
		}
		w.Write([]byte("{\"errcode\":0}"))
	} else if r.RequestURI == "/api/downlogs" {
		outFile, err := os.Create("logs.zip")
		if err != nil {
			w.Write([]byte(err.Error()))
			return
		}
		zw := zip.NewWriter(outFile)
		walkDir("logs", zw)
		zw.Close()
		w.Header().Set("Content-Type", mime.TypeByExtension(".zip"))
		outFile.Close()
		outFile, err = os.Open("logs.zip")
		if err != nil {
			w.Write([]byte(err.Error()))
			return
		}
		http.ServeContent(w, r, "logs.zip", time.Now(), outFile)
	} else if r.RequestURI == "/api/clearlogs" {
		RemoveDirs("logs/out")
		RemoveDirs("logs/err")
		w.Write([]byte("{\"errcode\":0}"))
	} else {
		w.Write(html)
	}
}

func walkDir(dir string, zw *zip.Writer) {
	files, err := os.ReadDir(dir)
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, file := range files {
		filename := dir + "/" + file.Name()
		if file.IsDir() {
			walkDir(filename, zw)
		} else {
			fz, err := zw.Create(filename)
			if err != nil {
				fmt.Println(err)
				continue
			}
			fo, err := os.Open(filename)
			if err != nil {
				fmt.Println(err)
				continue
			}
			io.Copy(fz, fo)
			fo.Close()
		}
	}
}

func RemoveDirs(dir string) {
	files, err := os.ReadDir(dir)
	if err != nil {
		return
	}
	for _, file := range files {
		if file.IsDir() {
			os.RemoveAll(dir + "/" + file.Name())
		}
	}
}
